﻿using Antlr4.Runtime;
using Framework.Components;
using Framework.Utils;
using NVelocity;
using NVelocity.App;
using NVelocity.Runtime;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using tproto.antlr4;
using Tproto.Maker;

namespace tproto.Maker
{
    /// <summary>
    /// 参数枚举
    /// </summary>
    enum TprotoParams
    {
        [TprotoParam("-in")]
        InputPath,
        
        [TprotoParam("-out")]
        OutputPath,

        [TprotoParam("-vmargs")]
        VmFile,

        [TprotoParam("-vmargs")]
        VmArgs,

        [TprotoParam("-outfilename")]
        OutFileName,

        [TprotoParam("-of")]
        OpenFolder,

    }

    public class TprotoParamAttribute : Attribute
    {
        /// <summary>
        /// 说明
        /// </summary>
        public string Desc;

        /// <summary>
        /// 参数名
        /// </summary>
        public string ArgKey;


        public TprotoParamAttribute(string argKey, string desc = "")
        {
            this.Desc = desc;
            ArgKey = argKey;
        }
    }

    public class TprotoMaker : FileMaker
    {

        protected override int Execute(Dictionary<object, object> paramArgs)
        {
            var inputPath = (string)paramArgs[TprotoParams.InputPath];
            FileUtils.ActionFolder(inputPath, "*", (FileSystemInfo info) =>
            {
                if (info is DirectoryInfo) { return true; }
                if (info is FileInfo)
                {
                    Make((FileInfo)info, null, paramArgs);
                }
                return true;
            });

            
            // 打开文件夹
            if ((bool)paramArgs[TprotoParams.OpenFolder])
            {
                string outpath = (string)paramArgs[TprotoParams.OutputPath];
                FileUtils.OpenFolder(outpath);
            }
            return 0;
        }

        protected void Make(FileInfo file, string path, Dictionary<object, object> paramArgs)
        {
            // Log.Info(file.FullName);
            string fileStr = FileUtils.LoadFile(file.FullName, "UTF-8");

            // 词法解析
            var stream = new AntlrInputStream(fileStr);
            var lexer = new tprotoLexer(stream);
            var tokens = new CommonTokenStream(lexer);
            var parser = new tprotoParser(tokens);
            var tree = parser.program();

            // 执行转化
            var visitor = new TprotoVisitor();
            // Log.Info("tree:" + tree.ToStringTree(parser));
            var fileResult = visitor.Visit(tree);

            // 模板导出            
            VelocityEngine vltEngine = new VelocityEngine();
            vltEngine.Init();

            // load
            string vmfile = (string)paramArgs[TprotoParams.VmFile];
            string vmFileStr = FileUtils.LoadFile(vmfile, "UTF-8");
            Template vltTemplate = vltEngine.GetTemplate(vmfile, "UTF-8");

            // init context
            VelocityContext vltContext = new VelocityContext();
            vltContext.Put("file", fileResult);
            vltContext.Put("helper", new VelocityHelper());
            vltContext.Put("tproto", fileResult.Context);

            var vmArgs = (Dictionary<string, string>)paramArgs[TprotoParams.VmArgs];
            foreach (var entry in vmArgs)
            {
                string akey = "args_" + entry.Key;
                vltContext.Put(akey, entry.Value);
            }

            // write
            StringWriter vltWriter = new StringWriter();
            vltTemplate.Merge(vltContext, vltWriter);

            // write file
            string outpath = (string)paramArgs[TprotoParams.OutputPath];
            string outfilename = (string)paramArgs[TprotoParams.OutFileName];
            string savepath = outpath + "/" + string.Format(outfilename, Path.GetFileNameWithoutExtension(file.Name), fileResult.Package);
            FileUtils.SaveFile(savepath, vltWriter.ToString(), "UTF-8", false);

        }

        protected override Dictionary<object, object> GetParams(Dictionary<string, string> inputArgs)
        {
            // 默认参数设置
            var paramArgs = new Dictionary<object, object>();
            paramArgs[TprotoParams.InputPath] = ".";
            paramArgs[TprotoParams.OutputPath] = ".";

            string inpath = DictionaryUtils.Get(inputArgs, "-in", "."); // 遍历输入路径
            inpath = inpath.Replace("\\\\", "/");
            paramArgs[TprotoParams.InputPath] = inpath;

            string outpath = DictionaryUtils.Get(inputArgs, "-out", "."); // 输出路径
            outpath = outpath.Replace("\\\\", "/");
            paramArgs[TprotoParams.OutputPath] = outpath;

            // 创建输出路径
            System.IO.DirectoryInfo dinfo = new System.IO.DirectoryInfo(outpath);
            if (!dinfo.Exists)
            {
                dinfo.Create();
            }
            Log.Info("inpath=" + Path.GetFullPath(inpath));
            Log.Info("outpath=" + Path.GetFullPath(outpath));

            string vmfile = DictionaryUtils.Get(inputArgs, "-vm", "test.vm");
            vmfile = vmfile.Replace("\\\\", "/");
            if (!(new FileInfo(vmfile).Exists))
            {
                Log.Error("Not Find Vm File!" + vmfile);
                return null;
            }
            paramArgs[TprotoParams.VmFile] = vmfile;

            paramArgs[TprotoParams.OutFileName] = DictionaryUtils.Get(inputArgs, "-outfilename", "%s");
            paramArgs[TprotoParams.OpenFolder] = Utils.ToValue<bool>(DictionaryUtils.Get(inputArgs, "-openfolder", "false"));

            // 子参数解析
            var argsMap = new Dictionary<string, string>();
            string argsStr = DictionaryUtils.Get(inputArgs, "-vmargs", "."); ; // 其他参数
            if (!string.IsNullOrEmpty(argsStr))
            {
                argsStr = argsStr.Substring(1, argsStr.Length - 2); // 去掉[]
                argsMap = SystemUtils.ArgUtils.getArgs(argsStr);
            }
            paramArgs[TprotoParams.VmArgs] = argsMap;

            return paramArgs;
        }
    }

    public class VelocityHelper
    {
        public string GetFirstUpper(string str)
        {
            return StringUtils.ToTitleCase(str);
        }
    }


}
